1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package com.google.common.collect;
16
17 import static com.google.common.base.Preconditions.checkArgument;
18 import static com.google.common.base.Preconditions.checkNotNull;
19 import static com.google.common.collect.BoundType.CLOSED;
20 import static com.google.common.collect.BoundType.OPEN;
21
22 import com.google.common.annotations.GwtCompatible;
23 import com.google.common.base.Objects;
24
25 import java.io.Serializable;
26 import java.util.Comparator;
27
28 import javax.annotation.Nullable;
29
30
31
32
33
34
35
36
37
38
39 @GwtCompatible(serializable = true)
40 final class GeneralRange<T> implements Serializable {
41
42
43
44 static <T extends Comparable> GeneralRange<T> from(Range<T> range) {
45 @Nullable
46 T lowerEndpoint = range.hasLowerBound() ? range.lowerEndpoint() : null;
47 BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : OPEN;
48
49 @Nullable
50 T upperEndpoint = range.hasUpperBound() ? range.upperEndpoint() : null;
51 BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : OPEN;
52 return new GeneralRange<T>(Ordering.natural(), range.hasLowerBound(), lowerEndpoint,
53 lowerBoundType, range.hasUpperBound(), upperEndpoint, upperBoundType);
54 }
55
56
57
58
59 static <T> GeneralRange<T> all(Comparator<? super T> comparator) {
60 return new GeneralRange<T>(comparator, false, null, OPEN, false, null, OPEN);
61 }
62
63
64
65
66
67 static <T> GeneralRange<T> downTo(Comparator<? super T> comparator, @Nullable T endpoint,
68 BoundType boundType) {
69 return new GeneralRange<T>(comparator, true, endpoint, boundType, false, null, OPEN);
70 }
71
72
73
74
75
76 static <T> GeneralRange<T> upTo(Comparator<? super T> comparator, @Nullable T endpoint,
77 BoundType boundType) {
78 return new GeneralRange<T>(comparator, false, null, OPEN, true, endpoint, boundType);
79 }
80
81
82
83
84
85 static <T> GeneralRange<T> range(Comparator<? super T> comparator, @Nullable T lower,
86 BoundType lowerType, @Nullable T upper, BoundType upperType) {
87 return new GeneralRange<T>(comparator, true, lower, lowerType, true, upper, upperType);
88 }
89
90 private final Comparator<? super T> comparator;
91 private final boolean hasLowerBound;
92 @Nullable
93 private final T lowerEndpoint;
94 private final BoundType lowerBoundType;
95 private final boolean hasUpperBound;
96 @Nullable
97 private final T upperEndpoint;
98 private final BoundType upperBoundType;
99
100 private GeneralRange(Comparator<? super T> comparator, boolean hasLowerBound,
101 @Nullable T lowerEndpoint, BoundType lowerBoundType, boolean hasUpperBound,
102 @Nullable T upperEndpoint, BoundType upperBoundType) {
103 this.comparator = checkNotNull(comparator);
104 this.hasLowerBound = hasLowerBound;
105 this.hasUpperBound = hasUpperBound;
106 this.lowerEndpoint = lowerEndpoint;
107 this.lowerBoundType = checkNotNull(lowerBoundType);
108 this.upperEndpoint = upperEndpoint;
109 this.upperBoundType = checkNotNull(upperBoundType);
110
111 if (hasLowerBound) {
112 comparator.compare(lowerEndpoint, lowerEndpoint);
113 }
114 if (hasUpperBound) {
115 comparator.compare(upperEndpoint, upperEndpoint);
116 }
117 if (hasLowerBound && hasUpperBound) {
118 int cmp = comparator.compare(lowerEndpoint, upperEndpoint);
119
120 checkArgument(cmp <= 0, "lowerEndpoint (%s) > upperEndpoint (%s)", lowerEndpoint,
121 upperEndpoint);
122 if (cmp == 0) {
123 checkArgument(lowerBoundType != OPEN | upperBoundType != OPEN);
124 }
125 }
126 }
127
128 Comparator<? super T> comparator() {
129 return comparator;
130 }
131
132 boolean hasLowerBound() {
133 return hasLowerBound;
134 }
135
136 boolean hasUpperBound() {
137 return hasUpperBound;
138 }
139
140 boolean isEmpty() {
141 return (hasUpperBound() && tooLow(getUpperEndpoint()))
142 || (hasLowerBound() && tooHigh(getLowerEndpoint()));
143 }
144
145 boolean tooLow(@Nullable T t) {
146 if (!hasLowerBound()) {
147 return false;
148 }
149 T lbound = getLowerEndpoint();
150 int cmp = comparator.compare(t, lbound);
151 return cmp < 0 | (cmp == 0 & getLowerBoundType() == OPEN);
152 }
153
154 boolean tooHigh(@Nullable T t) {
155 if (!hasUpperBound()) {
156 return false;
157 }
158 T ubound = getUpperEndpoint();
159 int cmp = comparator.compare(t, ubound);
160 return cmp > 0 | (cmp == 0 & getUpperBoundType() == OPEN);
161 }
162
163 boolean contains(@Nullable T t) {
164 return !tooLow(t) && !tooHigh(t);
165 }
166
167
168
169
170 GeneralRange<T> intersect(GeneralRange<T> other) {
171 checkNotNull(other);
172 checkArgument(comparator.equals(other.comparator));
173
174 boolean hasLowBound = this.hasLowerBound;
175 @Nullable
176 T lowEnd = getLowerEndpoint();
177 BoundType lowType = getLowerBoundType();
178 if (!hasLowerBound()) {
179 hasLowBound = other.hasLowerBound;
180 lowEnd = other.getLowerEndpoint();
181 lowType = other.getLowerBoundType();
182 } else if (other.hasLowerBound()) {
183 int cmp = comparator.compare(getLowerEndpoint(), other.getLowerEndpoint());
184 if (cmp < 0 || (cmp == 0 && other.getLowerBoundType() == OPEN)) {
185 lowEnd = other.getLowerEndpoint();
186 lowType = other.getLowerBoundType();
187 }
188 }
189
190 boolean hasUpBound = this.hasUpperBound;
191 @Nullable
192 T upEnd = getUpperEndpoint();
193 BoundType upType = getUpperBoundType();
194 if (!hasUpperBound()) {
195 hasUpBound = other.hasUpperBound;
196 upEnd = other.getUpperEndpoint();
197 upType = other.getUpperBoundType();
198 } else if (other.hasUpperBound()) {
199 int cmp = comparator.compare(getUpperEndpoint(), other.getUpperEndpoint());
200 if (cmp > 0 || (cmp == 0 && other.getUpperBoundType() == OPEN)) {
201 upEnd = other.getUpperEndpoint();
202 upType = other.getUpperBoundType();
203 }
204 }
205
206 if (hasLowBound && hasUpBound) {
207 int cmp = comparator.compare(lowEnd, upEnd);
208 if (cmp > 0 || (cmp == 0 && lowType == OPEN && upType == OPEN)) {
209
210 lowEnd = upEnd;
211 lowType = OPEN;
212 upType = CLOSED;
213 }
214 }
215
216 return new GeneralRange<T>(comparator, hasLowBound, lowEnd, lowType, hasUpBound, upEnd, upType);
217 }
218
219 @Override
220 public boolean equals(@Nullable Object obj) {
221 if (obj instanceof GeneralRange) {
222 GeneralRange<?> r = (GeneralRange<?>) obj;
223 return comparator.equals(r.comparator) && hasLowerBound == r.hasLowerBound
224 && hasUpperBound == r.hasUpperBound && getLowerBoundType().equals(r.getLowerBoundType())
225 && getUpperBoundType().equals(r.getUpperBoundType())
226 && Objects.equal(getLowerEndpoint(), r.getLowerEndpoint())
227 && Objects.equal(getUpperEndpoint(), r.getUpperEndpoint());
228 }
229 return false;
230 }
231
232 @Override
233 public int hashCode() {
234 return Objects.hashCode(comparator, getLowerEndpoint(), getLowerBoundType(), getUpperEndpoint(),
235 getUpperBoundType());
236 }
237
238 private transient GeneralRange<T> reverse;
239
240
241
242
243 GeneralRange<T> reverse() {
244 GeneralRange<T> result = reverse;
245 if (result == null) {
246 result = new GeneralRange<T>(
247 Ordering.from(comparator).reverse(), hasUpperBound, getUpperEndpoint(),
248 getUpperBoundType(), hasLowerBound, getLowerEndpoint(), getLowerBoundType());
249 result.reverse = this;
250 return this.reverse = result;
251 }
252 return result;
253 }
254
255 @Override
256 public String toString() {
257 return new StringBuilder()
258 .append(comparator)
259 .append(":")
260 .append(lowerBoundType == CLOSED ? '[' : '(')
261 .append(hasLowerBound ? lowerEndpoint : "-\u221e")
262 .append(',')
263 .append(hasUpperBound ? upperEndpoint : "\u221e")
264 .append(upperBoundType == CLOSED ? ']' : ')')
265 .toString();
266 }
267
268 T getLowerEndpoint() {
269 return lowerEndpoint;
270 }
271
272 BoundType getLowerBoundType() {
273 return lowerBoundType;
274 }
275
276 T getUpperEndpoint() {
277 return upperEndpoint;
278 }
279
280 BoundType getUpperBoundType() {
281 return upperBoundType;
282 }
283 }